import numpy as np

def get_class_recs(dataset, class_name):
    class_idx = dataset.class_map[class_name]

    class_recs = {}
    npos = 0  # true num of objects
    # print anno

    for id in range(len(dataset)):
        w,h = dataset.patch_pos[id][-2:]
        bbox = [box[:-1] for box in dataset.bboxs_pos[id] if box[-1] == class_idx]   # float local
        det  = [False] * len(bbox)
        npos += len(bbox)

        bbox = np.array(bbox)
        bbox[:,0] = int(bbox[:,0] * w)
        bbox[:, 1] = int(bbox[:, 1] * h)
        bbox[:,2] = int(bbox[:,2] * w)
        bbox[:,3] = int(bbox[:,3] * h)
        class_recs[id] = {'bbox': np.array(bbox), 'det': det}

    return class_recs, npos, class_idx+1


def cal_class_ap(class_dets, class_recs, npos, ovthresh, use_07_metric=True):
    '''
    :param class_dets: np arr of (n,6)  6: [id, confi, xmin, ymin, xmax, ymax]
    :param class_recs:   class_recs[id] = {'bbox': np.array(bbox), 'det': det}
    :param npos:
    :param ovthresh:
    :param use_07_metric:
    :return:
    '''
    if len(class_dets) > 0:

        # sort by confidence
        sorted_ind = np.argsort(-class_dets[:, 1])
        sorted_scores = np.sort(-class_dets[:, 1])
        BB = class_dets[sorted_ind, 2:]
        image_ids = class_dets[sorted_ind, 0]

        # go down dets and mark TPs and FPs
        nd = len(image_ids)
        tp = np.zeros(nd)
        fp = np.zeros(nd)

        for d in range(nd):
            # print class_recs
            # print image_ids[d]
            # print class_recs[image_ids[d]]
            R = class_recs[image_ids[d]]
            bb = BB[d, :].astype(float)
            ovmax = -np.inf
            BBGT = R['bbox'].astype(float)
            if BBGT.size > 0:
                # compute overlaps
                # intersection
                ixmin = np.maximum(BBGT[:, 0], bb[0])
                iymin = np.maximum(BBGT[:, 1], bb[1])
                ixmax = np.minimum(BBGT[:, 2], bb[2])
                iymax = np.minimum(BBGT[:, 3], bb[3])
                iw = np.maximum(ixmax - ixmin, 0.)
                ih = np.maximum(iymax - iymin, 0.)
                inters = iw * ih
                uni = ((bb[2] - bb[0]) * (bb[3] - bb[1]) +
                       (BBGT[:, 2] - BBGT[:, 0]) *
                       (BBGT[:, 3] - BBGT[:, 1]) - inters)
                overlaps = inters / uni
                ovmax = np.max(overlaps)
                jmax = np.argmax(overlaps)

            if ovmax > ovthresh:  # if greater than ovthresh
                if not R['det'][jmax]:   # if have not been detected yet
                    # print 'tp+1: ovmax:%.4f'%(ovmax)
                    tp[d] = 1.
                    R['det'][jmax] = 1
                else:
                    # print 'duplicate detect fp+1: ovmax:%.4f'%(ovmax)
                    fp[d] = 1.
            else:
                # print 'fp+1: ovmax:%.4f'%(ovmax)
                fp[d] = 1.
        print 'tp-%d, fp-%d'%(tp.sum(),fp.sum())
        # compute precision recall
        fp = np.cumsum(fp)
        tp = np.cumsum(tp)
        rec = tp / float(npos)
        # avoid divide by zero in case the first detection matches a difficult
        # ground truth
        prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)
        ap = voc_ap(rec, prec, use_07_metric=use_07_metric)
    else:
        # print 'None detection'
        rec = -1.
        prec = -1.
        ap = -1.

    return rec, prec, ap


def voc_ap(rec, prec, use_07_metric=True):
    """ ap = voc_ap(rec, prec, [use_07_metric])
    Compute VOC AP given precision and recall.
    If use_07_metric is true, uses the
    VOC 07 11 point method (default:False).
    """
    if use_07_metric:
        # 11 point metric
        ap = 0.
        for t in np.arange(0., 1.1, 0.1):
            if np.sum(rec >= t) == 0:
                p = 0
            else:
                p = np.max(prec[rec >= t])
            ap = ap + p / 11.
    else:
        # correct AP calculation
        # first append sentinel values at the end
        mrec = np.concatenate(([0.], rec, [1.]))
        mpre = np.concatenate(([1.], prec, [0.]))

        # compute the precision envelope
        for i in range(mpre.size - 1, 0, -1):
            mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])

        # to calculate area under PR curve, look for points
        # where X axis (recall) changes value
        i = np.where(mrec[1:] != mrec[:-1])[0]

        # and sum (\Delta recall) * prec
        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
    return ap


